
package co.fitstart.data.web.interceptor;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.util.WebUtils;

import co.fitstart.data.Constant;

public class WechatAuthorityInterceptor extends HandlerInterceptorAdapter {
    
    private static final Logger logger = LoggerFactory.getLogger(WechatAuthorityInterceptor.class);
    
    private static final String USER_AGENT = "User-Agent";
    
    private static final String MICRO_MESSENGER = "MicroMessenger";
    
    private static final String SAVED_REQUEST = "WECHAT_WEB_AUTHORITY_SAVED_REQUEST";
    
    /**
     * Flags for development mock
     */
    private boolean mock = false;
    
    /**
     * WeChat openid key stored in the cookie
     */
    private String openidKey = Constant.OPENID_KEY;
    
    /**
     * User Account key stored in the cookie
     */
    private String userAccountKey = Constant.USER_ACCOUNT_KEY;
    
    /**
     * WeChat openid key stored in the cookie
     */
    private String wechatAuthUrl;
    
    /**
     * URL to display general page describes "require WeChat browser environment"
     */
    private String requireWechatUrl;
    
    /**
     * @param mock the mock to set
     */
    public void setMock(boolean mock) {
        this.mock = mock;
    }
    
    /**
     * @param openidKey the openidKey to set
     */
    public void setOpenidKey(String openidKey) {
        this.openidKey = openidKey;
    }
    
    /**
     * @param wechatAuthUrl the wechatAuthUrl to set
     */
    public void setWechatAuthUrl(String wechatAuthUrl) {
        this.wechatAuthUrl = wechatAuthUrl;
    }
    
    /**
     * @param requireWechatUrl the requireWechatUrl to set
     */
    public void setRequireWechatUrl(String requireWechatUrl) {
        this.requireWechatUrl = requireWechatUrl;
    }
    
    /*
     * (non-Javadoc)
     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        
        if (handler instanceof HandlerMethod) {
            
            // 1. show debug log if current runtime is debug
            if(logger.isDebugEnabled()) {
                logger.debug("WechatAuthorityInterceptor#preHandle - To determine this request can be visited in addition to wechat client");
            }
            
            // find wechatSniffing from handlerMethod & check whether wechatClient exit or not
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            WechatAuthority wechatAuthority = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), WechatAuthority.class);
            
            if(wechatAuthority == null) {            // wechatSniffing not exit return super preHandle 
                logger.info("WechatAuthorityInterceptor#preHandle - wechatAuthority interceptor is null.");
                return super.preHandle(request, response, handler);
            }
            
            // 2.1 generate requiredUrl url
            String requiredUrl = StringUtils.isBlank(wechatAuthority.targetUrl()) ? ( this.requireWechatUrl == null ? "" : this.requireWechatUrl ) : wechatAuthority.targetUrl();
            
            // wechatClient exit, start check and determine this request can be visited or not
            logger.info("WechatAuthorityInterceptor#preHandle - start check user agent and determine this request can be visited in addition to wechat client");
            String userAgent = request.getHeader(USER_AGENT);
            
            logger.trace("WechatAuthorityInterceptor#preHandler() - Get user agent [{}].", userAgent);
            boolean isMicroMessenger = StringUtils.isNotBlank(userAgent) && userAgent.indexOf(MICRO_MESSENGER) >= 0;
            
            // 3. not comes from a wechat client, redirect to resolved target url
            if(!isMicroMessenger && !this.mock) {
                
                logger.info("WechatAuthorityInterceptor#preHandle - current user agent is not microMessenger, return to required url.");
                String resolvedTargetUrl = requiredUrl.startsWith("http") ? requiredUrl : (com.demo2do.core.web.utils.WebUtils.getRealServerPath(request) + requiredUrl);
                logger.trace("WechatAuthorityInterceptor#preHandle() - The resolved target URL is [{}].", resolvedTargetUrl);
                response.sendRedirect(resolvedTargetUrl);
                
                return false;
            }
            
            // 4. comes from a wechat client, start check openid in cookie 
            if(isMicroMessenger || this.mock) {  // comes from a wechat client or this is mock development
                
                logger.info("WechatAuthorityInterceptor#preHandle - current user agent is microMessenger");
                
                if(!this.needWechatAuthority(openidKey, userAccountKey, request)) {
                    
                    logger.warn("WechatAuthorityInterceptor#preHandle - get user openid cookie failed. go to wechat auth url to get openid and save into cookie");
                    
                    saveOriginalRequest(request);  // Save original request URL
                    
                    String resolvedAuthUrl = wechatAuthUrl.startsWith("http") ? wechatAuthUrl : (com.demo2do.core.web.utils.WebUtils.getRealServerPath(request) + wechatAuthUrl);
                    logger.trace("WechatAuthorityInterceptor#preHandle() - The resolved wechat auth URL is [{}].", resolvedAuthUrl);
                    
                    response.sendRedirect(resolvedAuthUrl);
                    return false;
                }
            }
        }
        
        return super.preHandle(request, response, handler);
    }
    
    /**
     * Save user mobile into cookie by wechat openid
     * 
     * @param request
     * @param response
     * @param wechatClient
     * @return
     */
    private boolean needWechatAuthority(String openidKey, String userAccountKey, HttpServletRequest request) {
        
        // 2. start get openid from cookie by openid ket
        logger.info("WechatAuthorityInterceptor#hasOpenid - start get openid from cookie by openid key");
        
        // 3.1 get openid from cookie by openid key
        Cookie openidCookie = WebUtils.getCookie(request, openidKey);
        Cookie userAcccountCookie = WebUtils.getCookie(request, userAccountKey);
        
        if (openidCookie == null || userAcccountCookie == null) {
            logger.warn("WechatAuthorityInterceptor#hasOpenid - get null openid and userAccount id from cookie with openidKey [{}] userAccount key [{}]", openidKey, userAccountKey);
            return false;
        }
        
        logger.info("WechatAuthorityInterceptor#hasOpenid - get openid from cookie with key [{}] and value [{}]", openidKey, openidCookie.getValue());
        logger.info("WechatAuthorityInterceptor#hasOpenid - get userAccount from cookie with key [{}] and value [{}]", userAccountKey, userAcccountCookie.getValue());
        
        return true;
    }
    
    /**
     * Save original request in HTTP session for further reference
     * 
     * @param request
     */
    private void saveOriginalRequest(HttpServletRequest request) {
        
        // Construct original URL from request URI and query string
        String originalUri = com.demo2do.core.web.utils.WebUtils.getRealRequestURI(request);
        String queryString = request.getQueryString();
        
        String originalUrl = StringUtils.isBlank(queryString) ? originalUri : (originalUri + "?" + queryString);
        
        // Save the original URL in the session currently
        HttpSession httpSession = request.getSession();
        httpSession.setAttribute(SAVED_REQUEST, originalUrl);
    }
}
